﻿using BaseUtils;
using BaseUtils.Helper;
using BaseUtils.Logger;
using BaseUtils.Utils;
using Service.src.Flow.JavaScript;
using Service.src.Model.Folw;
using Service.src.NW.Enum;
using Service.src.NW.Model;
using Service.src.NW.Model.Enum;
using Service.src.NW.Model.Folw;
using Service.src.NW.Processor.Core;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Service.src.Flow.Server
{
    public class FlowProcessor : BaseProcessor
    {

        /// <summary>
        /// 获取流程
        /// </summary>
        /// <param name="FlowId"></param>
        /// <returns></returns>
        public static FlowRoot GetFlow(string Path, string FolwName)
        {
            FlowRoot Flow = FileHelper.ReadContent<FlowRoot>($"{Path}{FolwName}.nw");
            if (Flow == null)
            {
                throw new Exception("");
            }
            return Flow;
        }

        /// <summary>
        /// 根据流程ID获取流程信息
        /// </summary>
        /// <param name="FlowId"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static FlowRoot GetFlow(long FlowId)
        {
            if (FlowId <= 0)
            {
                throw new Exception("流程异常");
            }
            FlowBaseInfo BaseInfo = null;
            FlowRoot Flow = null;
            if (AppData.OpenFlows.ContainsKey(FlowId))
            {
                Flow = AppData.OpenFlows[FlowId];
            }
            else
            {
                BaseInfo = AppData.AllFlowBaseInfos[FlowId];
            
                if (BaseInfo == null)
                {
                    LoggerService.Error("流程地址读取异常，空地址");
                    return null;
                }
                Flow = FileHelper.ReadContent<FlowRoot>(AppData.AppPath + BaseInfo.FilePath +"//"+ BaseInfo.FileName);
                if (Flow == null)
                {
                    LoggerService.Error("流程读取异常，空或未读取");
                    return null;
                }
                Flow.name = BaseInfo.FileName;
                Flow = DataPreprocessing(Flow);

                AppData.AddOpenFlow(Flow);
            }

            if (Flow == null)
            {
                throw new Exception("找不到流程");
            }

            return Flow;
        }

        public static FlowRoot GetFlow2(string FolwPath)
        {
            FlowRoot Flow = FileHelper.ReadContent<FlowRoot>($"{FolwPath}");
            if (Flow == null)
            {
                throw new Exception("");
            }
            return Flow;
        }
        /// <summary>
        /// 更新流程
        /// 
        /// 同时通知UI更新
        /// </summary>
        /// <param name="Flow"></param>
        /// <returns></returns>
        public static void UpdateFlow(FlowRoot Flow, bool UpdateUI = true)
        {
            FlowBaseInfo flowInfo = AppData.GetFlowById(Flow.Id);
            if (flowInfo == null)
            {
                throw new Exception("出错了，找不到流程");
            }
            string fj = Flow.ToJson(new[] { "Children", "Parents", "ParentKeyValue", "TotalSeconds", "Loggers", "Code", "nodeStatus", "NodeOutParam", "Logger", "backgroundColor", "NodeTotalSeconds" });

            FileHelper.WriteContent($"{AppData.AppPath}{flowInfo.FilePath}//", flowInfo.FileName, fj);
            FlowRoot f = null;
            AppData.OpenFlows.TryRemove(Flow.Id, out f);
            if (UpdateUI)
            {
                JavaScriptEvent.Send(JavaScriptEventEnum.UPDATE_FLOW_DATA, fj);
            }
        }

        private static object LockRunings = new object();

        /// <summary>
        /// 执行流程
        /// </summary>
        /// <param name="FolwId"></param>
        internal static  void RunFlow(long FlowId)
        {
            if (!BaseService.AppConfig.field.EnableFlow)
            {
                LoggerService.Error($"流程执行被禁用");
                return;
            }
            if ( !AppData.AppRunSuccess )
            {
                LoggerService.Error($"APP未启动，等待启动完成后重试");
                return;
            }
            if (FlowId <= 0  || AppData.Runings.ContainsKey(FlowId))
            {
                LoggerService.Error($"流程未结束，无法执行下个流程，流程id={FlowId}，执行中的流程={AppData.Runings?.ToJson()}");
                return;
            }
            Stopwatch SW = Stopwatch.StartNew();
            FlowRoot Flow = GetFlow(FlowId);
            if (Flow == null)
            {
                LoggerService.Error($"流程执行异常，请检查是否有对应的流程，流程id：{FlowId}");
                return;
            }
            lock (LockRunings)
            {
                if (!AppData.Runings.ContainsKey(FlowId))
                {
                    try
                    {
                        AppData.Runings.TryAdd(FlowId, Flow);
                    }
                    catch (IndexOutOfRangeException ex)
                    {
                        LoggerService.Error("越界了：" + ex.Message, ex);
                        LoggerService.Error($"Flow对象的相关属性值：[属性={Flow.ToJson()}, ");
                        LoggerService.Error(ex.StackTrace);
                        LoggerService.Error($"在添加FlowId为 {FlowId} 的元素时发生索引越界异常");
                    }
                }
            }
            AppData.RuningNodes[FlowId] = new ConcurrentBag<int>();

            Init(Flow);
            Flow.stopwatch = SW;
            Flow.IsRuning = true;
            Flow.TreeProcessors.ForEach(t =>
            {
                AppData.AddRuningNodeId(FlowId, t.id);
                Task.Run(() =>
                {
                    try
                    {
                        Task FirstRun = t.ExecuteNode(FlowId);
                        if (FirstRun.Status == TaskStatus.Faulted)
                        {
                            t.NodeStatus = NodeStatusEnum.ERROR;
                            Complete(t, NodeStatusEnum.ERROR);
                        }
                        LoggerService.Info($"记录节点状态：{t.NodeStatus}");
                    }
                    catch (Exception ex)
                    {
                        t.NodeStatus = NodeStatusEnum.ERROR;
                        Complete(t, NodeStatusEnum.ERROR);
                        LoggerService.Error($"节点执行异常，节点id：{t.id},原因：{ex.Message}", ex);
                    }
                });
            });
            OnRuning(Flow);
        }

        private static void Init(FlowRoot Flow)
        {
            Flow.processors.ForEach(f =>
            {
                f.ExecutionSource = new TaskCompletionSource<bool>();
                f.NodeStatus = NodeStatusEnum.DEFAULT;
                f.Logger = new NodeLogger();
                f.stopwatch = new Stopwatch();
                f.style.backgroundColor = "#fff";
                foreach (NodeProcessor item in f.Children)
                {
                    item.ExecutionSource = new TaskCompletionSource<bool>();
                    item.NodeStatus = NodeStatusEnum.DEFAULT;
                    item.Logger = new NodeLogger();
                    item.stopwatch = new Stopwatch();
                    item.style.backgroundColor = "#fff";
                }
            });
        }

        /// <summary>
        /// 监听运行中的流程
        /// </summary>
        public static void OnRuning(FlowRoot Flow)
        {
            long FlowId = Flow.Id;
            _ = Task.Run(() =>
            {
                while ((bool)(AppData.Runings?.ContainsKey(FlowId)))
                {
                    bool ContainsKey = AppData.RuningNodes.ContainsKey(FlowId);
                    ConcurrentBag<int> NodeIds = new ConcurrentBag<int>();
                    if (AppData.RuningNodes.ContainsKey(FlowId))
                    {
                        NodeIds = AppData.RuningNodes[FlowId];
                    }
                    if (NodeIds?.Count > 0)
                    {
                        NodeIds = new ConcurrentBag<int>(NodeIds.Where(n => n != 0));
                    }
                    if (ContainsKey && NodeIds?.Count == 0)
                    {
                        try
                        {
                            StopFlow(FlowId);
                        }
                        catch (Exception ex)
                        {
                            LoggerService.Error($"监听流程结束时异常：{ex.Message}", ex);
                        }
                    }

                    FlowOutTimeExit(Flow);
                    Thread.Sleep(55);
                }
            });
        }

        /// <summary>
        /// 流程超时退出
        /// </summary>
        /// <param name="Flow"></param>
        private static void FlowOutTimeExit(FlowRoot Flow)
        {
            FlowBaseInfo item = AppData.AllFlowBaseInfos[Flow.Id];
            if (Flow.stopwatch.Elapsed.TotalSeconds > item.OutTime/1000)
            {
                StopFlow(item.id);
                LoggerService.Warning($"流程超时自动停止,流程ID = {Flow.Id}");
            }
        }

        /// <summary>
        /// 终止流程
        /// 
        /// IsAuto = true 自动
        /// IsAuto = false 手动
        /// </summary>
        /// <param name="FlowId"></param>
        /// <param name="IsAuto"></param>
        /// <returns></returns>
        internal static bool StopFlow(long FlowId)
        {
            if (!AppData.Runings.ContainsKey(FlowId))
            {
                return true;
            }
            FlowRoot Flow = AppData.Runings[FlowId];
            Flow.stopwatch.Stop();
            TimeSpan elapsedTime = Flow.stopwatch.Elapsed;
            Flow.FlowTotalSeconds = elapsedTime.TotalSeconds;

            FlowRoot f = null;
            AppData.Runings?.TryRemove(FlowId, out f);
            if (AppData.RuningNodes.ContainsKey(FlowId))
            {
                int RemoveNode;
                while (AppData.RuningNodes[FlowId].TryTake(out RemoveNode)) { }
            }
            //AppData.RuningNodes[FlowId].Clear();

            ConcurrentBag<int> ids = new ConcurrentBag<int>();
            AppData.RuningNodes.TryRemove(FlowId, out ids);
            JavaScriptEvent.Send(JavaScriptEventEnum.FLOW_COMPLETE, Flow);
            //JavaScriptEvent.Send(JavaScriptEventEnum.FLOW_COMPLETE, IsAuto? "流程终止" : "流程被手动终止");
            return !AppData.Runings.ContainsKey(FlowId);
        }

        /// <summary>
        /// 流程结束
        /// </summary>
        /// <param name="sW"></param>
        /// <exception cref="NotImplementedException"></exception>
        private static void FlowEnd(FlowRoot Flow, Stopwatch SW)
        {
            SW.Stop();
            Flow.FlowTotalSeconds = SW.Elapsed.TotalSeconds;
            JavaScriptEvent.Send(JavaScriptEventEnum.FLOW_COMPLETE, Flow);
        }


        /// <summary>
        /// 转换数据结构
        /// </summary>
        /// <param name="Flow"></param>
        /// <returns></returns>
        private static FlowRoot DataPreprocessing(FlowRoot Flow)
        {

            //Flow.processors.Distinct().ToList().Sort((id1, id2) => id1.id.CompareTo(id2.id));
            Dictionary<int, NodeProcessor> processors = Flow.processors.ToDictionary(p => p.id, p =>
               {
                   p.NodeStatus = NodeStatusEnum.DEFAULT;
                   GetNodeDevice(p);
                   p.SetNodeOutParamName();
                   return p;
               });

            foreach (Connection connection in Flow.connections)
            {
                if (!processors.ContainsKey(connection.source) || !processors.ContainsKey(connection.target))
                {
                    continue;
                }
                NodeProcessor sourceProcessor = processors[connection.source];
                NodeProcessor targetProcessor = processors[connection.target];

                if (sourceProcessor.Children == null)
                {
                    sourceProcessor.Children = new List<NodeProcessor>();
                }
                sourceProcessor.Children.Add(targetProcessor);

                if (targetProcessor.Parents == null)
                {
                    targetProcessor.Parents = new List<NodeProcessor>();
                }
                targetProcessor.Parents.Add(sourceProcessor);
                //targetProcessor.ParentKeyValue.Add(sourceProcessor.id, sourceProcessor);
                //SetParentKeyValue(targetProcessor, sourceProcessor);
            }

            List<NodeProcessor> tree = processors.Values.Where(p =>
                !Flow.connections.Any(c => c.target == p.id)).ToList();

            Flow.TreeProcessors = tree;

            foreach (NodeProcessor item in Flow.processors)
            {
                SetParentKeyValue(item, item);
            }
            return Flow;
        }

        private static void SetParentKeyValue(NodeProcessor target, NodeProcessor source)
        {
            if (source.Parents != null)
            {
                foreach (NodeProcessor item in source.Parents)
                {
                    if (!target.ParentKeyValue.ContainsKey(item.id))
                    {
                        target.ParentKeyValue.Add(item.id, item);
                    }
                    SetParentKeyValue(target, item);
                }
            }
        }

        /// <summary>
        /// 设置节点的设备
        /// </summary>
        /// <param name="p"></param>
        private static void GetNodeDevice(NodeProcessor p)
        {
            int DataSourceId = p?.NodeInputParam?.NodePLC?.DataSourceId ?? -1;
            if (p.PLCDevice != null || p.NodeInputParam == null || !AppData.IPLCHelpers.ContainsKey(DataSourceId))
            {
                return;
            }

            if (p.nodeType == NodeTypeEnum.PLC_WRITE)
            {
                p.PLCDevice = AppData.IPLCHelpers[DataSourceId];
            }
            if (p.nodeType == NodeTypeEnum.PLC_READ)
            {
                p.PLCDevice = AppData.IPLCHelpers[DataSourceId];
            }
            //if (p.NodeInputParam.DataSourceId > 0)
            //{
            //    p.PLCDevice = AppService.IPLCHelpers[p.NodeInputParam.DataSourceId];
            //}

        }
    }
}
